﻿using System;
using System.Collections.Generic;

namespace QFramework
{
    public class SlotGroup
    {
        public SlotGroup() { }
        public SlotGroup(string key)
        {
            Key = key;
        }

        public string Key = string.Empty;

        public List<Slot> Slots => mSlots;

        private readonly List<Slot> mSlots = new();

        private Func<IItem, bool> mCondition = _ => true;

        #region slot
        public SlotGroup CreateSlot(IItem item, int count)
        {
            mSlots.Add(new Slot(item, count, this));
            return this;
        }
        public SlotGroup CreateSlot(IItem item, int count, out Slot slot)
        {
            slot = new Slot(item, count, this);
            mSlots.Add(slot);
            return this;
        }

        public SlotGroup CreateSlotsByCount(int count)
        {
            for (int i = 0; i < count; i++) CreateSlot(null, 0);
            return this;
        }

        public SlotGroup Condition(Func<IItem, bool> condition)
        {
            mCondition = condition;
            return this;
        }

        public bool CheckCondition(IItem item)
        {
            return mCondition(item);
        }
        #endregion

        #region item
        public struct ItemOperatorResult
        {
            public bool Succeed;
            public int RemainCont;
            public MessageType MessageType;
        }

        public enum MessageType
        {
            Full,
        }

        public bool AddItem(string itemKey)
        {
            var item = ItemKit.DictItem[itemKey];
            return AddItem(item);
        }
        public ItemOperatorResult AddItem(string itemKey, int count)
        {
            var item = ItemKit.DictItem[itemKey];
            return AddItem(item, count);
        }
        public bool AddItem(IItem item)
        {
            var slot = FindAddableSlot(item.GetKey);
            if (slot == null)
            {
                return false;
            }
            slot.Count += 1;
            slot.OnChanged.Trigger();
            return true;
        }
        public ItemOperatorResult AddItem(IItem item, int count)
        {
            if (item.IsStackable && item.GetHasMaxStackableCount)
            {
                var slot = FindAddableHasMaxStackableCountSlot(item.GetKey);
                if (slot != null)
                {
                    var canAddCount = slot.Item.GetMaxStackableCount - slot.Count;
                    if (count <= canAddCount)
                    {
                        slot.Count += count;
                        slot.OnChanged.Trigger();
                        return new ItemOperatorResult() { Succeed = true, RemainCont = 0 };
                    }
                    else
                    {
                        slot.Count += canAddCount;
                        slot.OnChanged.Trigger();
                        count -= canAddCount;
                        return AddItem(item.GetKey, count);
                    }
                }
                else
                {
                    return new ItemOperatorResult() { Succeed = false, RemainCont = count };
                }
            }
            else
            {
                var slot = FindAddableSlot(item.GetKey);
                if (slot == null)
                {
                    return new ItemOperatorResult();
                }
                slot.Count += count;
                slot.OnChanged.Trigger();
                return new ItemOperatorResult() { Succeed = true, RemainCont = 0 };
            }
        }

        public bool SubItem(string itemKey, int count = 1)
        {
            var slot = FindSlotByKey(itemKey);
            if (slot == null || slot.Count < count)
            {
                return false;
            }
            slot.Count -= count;
            slot.OnChanged.Trigger();
            return true;
        }
        public bool SubItem(IItem item, int count = 1)
        {
            var slot = FindSlotByKey(item.GetKey);
            if (slot == null || slot.Count < count)
            {
                return false;
            }
            slot.Count -= count;
            slot.OnChanged.Trigger();
            return true;
        }
        #endregion

        #region private method
        private Slot FindSlotByKey(string itemKey)
        {
            return mSlots.Find(s => s.Item != null && s.Item.GetKey == itemKey && s.Count != 0);
        }
        private Slot FindEmptySlot()
        {
            return mSlots.Find(s => s.Count == 0);
        }
        private Slot FindAddableSlot(string itemKey)
        {
            var item = ItemKit.DictItem[itemKey];
            if (item.IsStackable)
            {
                if (item.GetHasMaxStackableCount)
                {
                    return FindAddableHasMaxStackableCountSlot(itemKey);
                }
                else
                {
                    var slot = FindSlotByKey(itemKey);
                    if (slot == null || (item.GetHasMaxStackableCount && item.GetMaxStackableCount == slot.Count))
                    {
                        slot = FindEmptySlot();
                        if (slot != null)
                        {
                            slot.Item = item;
                        }
                    }
                    return slot;
                }
            }
            else
            {
                var slot = FindEmptySlot();
                if (slot != null)
                {
                    slot.Item = item;
                }
                return slot;
            }
        }

        private Slot FindAddableHasMaxStackableCountSlot(string itemKey)
        {
            foreach (var slot in mSlots)
            {
                if (slot.Count != 0 && slot.Item.GetKey == itemKey && slot.Item.GetMaxStackableCount != slot.Count)
                {
                    return slot;
                }
            }
            var emptySlot = FindEmptySlot();
            if (emptySlot != null)
            {
                emptySlot.Item = ItemKit.DictItem[itemKey];
            }
            return emptySlot;
        }
        #endregion
    }
}
